﻿#include "videothread.h"
#include <QSemaphore>

/*************************************************************************
 *
 *  录像
 *
 */
#define FILE_SZ_1M      (1024LL*1024LL)
#define FILE_SZ_1G      (1024LL*FILE_SZ_1M)
#define FILE_SZ_3G      (3LL*FILE_SZ_1G)

VideoThread::VideoThread(VideoDevice* vd, QObject *parent) :
    QThread(parent),
    m_videoDevice(vd)
{
    m_videoSize = m_videoDevice->getPixelSize();
    m_videoFps  = m_videoDevice->getFrameRate();
    m_frameTime = 1000000 / m_videoFps;

    m_mppEncode = new MppEncoder(m_videoSize.width(), m_videoSize.height(), m_videoFps);

    m_checkSizeTimer = new QTimer(this);
    QObject::connect(m_checkSizeTimer, &QTimer::timeout, this, &VideoThread::checkRecordSizeSlot);
}

VideoThread::~VideoThread()
{
    setRecordStatus(RECORD_STOP);
    endRecord();
    delete m_mppEncode;
}

void VideoThread::stop()
{
    isStop = true;
    quit();
    wait();
}

void VideoThread::setRecordPath(const QString &path)
{
    if (path.isEmpty())
    {
        if (m_recordStatus != RECORD_NULL)
        {
            m_recordStatus = RECORD_NULL;
            int num = m_semVideo.available();
            if (num != 0)
                m_semVideo.acquire(num);
        }
        return ;
    }
    m_recordPath = path;
}

void VideoThread::setRecordStatus(RecordStatus status)
{
    if (m_recordPath.isEmpty())
        return ;

    RecordStatus record = getStatus();

    switch (record) {
    case RECORD_NULL:
    case RECORD_STOP:
    {
        if (status == RECORD_START)
        {
            m_frameCnt = 0;
            record = status;
        }
    }break;
    case RECORD_START:
    {
        if (status == RECORD_STOP)
            record = status;
    }break;
    case RECORDING:
    case RECORD_SIZEOVER:
    {
        if (status == RECORD_PAUSE ||
            status == RECORD_STOP)
            record = status;
    }break;
    case RECORD_PAUSE:
    {
        if (status == RECORD_STOP) {
            record = status;
            if (m_semVideo.available() == 0)
                m_semVideo.release();
        }
        else if (status == RECORDING)
        {
            record = status;
        }
    }break;
    }
    //定时器处理
    if (record == RECORD_PAUSE ||
        record == RECORD_STOP)
    {
        m_checkSizeTimer->stop();
    }
    if (record == RECORD_START ||
        record == RECORDING)
    {
        if (!m_checkSizeTimer->isActive())
            m_checkSizeTimer->start(10000);
    }
    setStatus(record);
}

void VideoThread::videoFrameSlot(void)
{
    RecordStatus record = getStatus();
    if (record == RECORD_NULL)
    {
        int num = m_semVideo.available();
        if (num != 0)
            m_semVideo.acquire(num);
        return ;
    }
    m_semVideo.release();
}

void VideoThread::startRecord()
{
    int videoCnt = 0;
    QString lo = m_recordPath + "/VIDEO" + QString::number(videoCnt) + ".mp4";
    QFileInfo fi = QFileInfo(lo);

    while(fi.isFile()){
        videoCnt++;
        lo = m_recordPath + "/VIDEO" + QString::number(videoCnt) + ".mp4";
        fi = QFileInfo(lo);
    }

    QByteArray mp4file(lo.toLocal8Bit());
    bool ret = MP4_Write_Init(mp4file.data(), m_videoFps);
    if(ret == false) {
        qDebug()<<"MP4_Write_Init failed..";
        return ;
    }
    QByteArray array = m_mppEncode->getFrameHeader();
    if (array.isEmpty())
        return ;

    emit encodeDataSignal(true, array);
    MP4_Write_SPS_PPS((uint8_t*)array.data(), array.length());
    m_recordFile = lo;
}

void VideoThread::endRecord()
{
    MP4_Write_Exit();
    sync();
}

void VideoThread::videoSave(const QByteArray &pArray)
{
    int lenght = pArray.length();
    MP4_Write_Main((uint8_t*)pArray.data(), lenght);
}

void VideoThread::recordTiming()
{
    m_frameCnt++;
    quint64 time = (m_frameCnt * m_frameTime) / 1000000; //s
    if (m_frameTimeSave != time)
    {
        m_frameTimeSave = time;
        QString timeStr = QTime(0, 0, 0).addSecs(time).toString(QString::fromLatin1("HH:mm:ss"));
        //qDebug()<<"timeStr="<<timeStr;
        emit videoTimeSignal(timeStr);
    }
}

void VideoThread::checkRecordSizeSlot()
{
    if (m_recordFile.isEmpty())
        return ;

    RecordStatus record = getStatus();
    if (record != RECORD_START &&
        record != RECORDING)
        return ;

    QFileInfo fi = QFileInfo(m_recordFile);
    qint64 size = fi.size();
    if (size > FILE_SZ_3G){
        qDebug()<<"size is 3G!";
        setStatus(RECORD_SIZEOVER);
    }
}

void VideoThread::run()
{
    msleep(250);

    for ( ; 1 ;)
    {
        if(isStop == true)
            return;
        if(m_videoDevice == nullptr)
            continue;
        m_semVideo.acquire();

        //数据保存
        RecordStatus record = getStatus();
        if (record == RECORD_SIZEOVER)
        {
            setStatus(RECORD_START);
            MP4_Write_Exit();
        }
        if (record == RECORD_START)
        {
            setStatus(RECORDING);
            startRecord();
        }
        if (record == RECORDING)
        {
            //编码
            void * desBuf = m_mppEncode->getFrameBuffer();
            m_videoDevice->getFrameData(desBuf);
            QByteArray encodeArray;
            m_mppEncode->encode(encodeArray);
            emit encodeDataSignal(false, encodeArray);
            recordTiming();
            videoSave(encodeArray);
        }
        if (record == RECORD_STOP)
        {
            setStatus(RECORD_NULL);
            endRecord();
            emit videoFinishSignal();
        }
    }
}

/*************************************************************************
 *
 *  拍照
 *
 */

PictureThread::PictureThread(VideoDevice* vd, QObject *parent) :
    QThread(parent),
    m_videoDevice(vd)
{

}

void PictureThread::stop()
{
    isStop = true;
    quit();
    wait();
}

void PictureThread::setPicturePath(const QString &path)
{
    if (path.isEmpty())
    {
        if (m_pictureStatus != PICTURE_NULL)
        {
            m_pictureStatus = PICTURE_NULL;
            int num = m_semPicture.available();
            if (num != 0)
                m_semPicture.acquire(num);
        }
        return ;
    }
    m_picturePath = path;
}

void PictureThread::setPictureStatus(PictureStatus status)
{
    if (status == PICTURE_NULL)
        return ;
    if (m_picturePath.isEmpty())
        return ;
    if (m_pictureStatus == PICTURE_NULL)
        m_pictureStatus = status;
}

QString PictureThread::openPictureFile()
{
    QString lo = "";

    if (m_picturePath.isEmpty())
        return lo;

    int imageCnt = 0;
    lo = m_picturePath + "/PIC" + QString::number(imageCnt) + ".jpg";
    QFileInfo fi = QFileInfo(lo);

    while(fi.isFile()){
        imageCnt++;
        lo = m_picturePath + "/PIC" + QString::number(imageCnt) + ".jpg";
        fi = QFileInfo(lo);
    }

    return lo;
}

void PictureThread::pictureFrameSlot()
{
    if (m_pictureStatus == PICTURE_NULL)
    {
        int num = m_semPicture.available();
        if (num != 0)
            m_semPicture.acquire(num);
        return ;
    }
    m_semPicture.release();
}

void PictureThread::run()
{
    msleep(250);

    VideoFfmpeg ffmpeg(CAMERA_PIXEL_WIDE, CAMERA_PIXEL_HIGH);
    int rgbLenght = ffmpeg.getNumBytes();

    for ( ; 1 ;)
    {
        if(isStop == true)
            return;
        if(m_videoDevice == nullptr)
            continue;
        m_semPicture.acquire();

        if (m_pictureStatus != PICTURE_START)
            continue;

        m_pictureStatus = PICTURE_NULL;

        QByteArray array;
        array.clear();
        m_videoDevice->getFrameData(array);
        ffmpeg.setFrameYUV((unsigned char *)array.data());

        char * dstBuff = (char *)malloc(rgbLenght * sizeof(uint8_t));
        memset(dstBuff, 0, rgbLenght * sizeof(uint8_t));
        ffmpeg.setRgbBuffer((unsigned char *)dstBuff);
        bool isOk = ffmpeg.play();
        if (isOk == false)
        {
            qDebug()<<"ffmpeg conversion failed!";
            delete dstBuff;
            dstBuff = nullptr;

            continue;
        }
        //获取文件名称
        QString fileName = openPictureFile();
        //保存
        if (!fileName.isEmpty()){
            QSize s = m_videoDevice->getPixelSize();
            QImage tmpImg((unsigned char*)dstBuff, s.width(), s.height(), QImage::Format_RGB888);
           tmpImg.save(fileName);
           sync();
           //拍照保存完成
           emit pictureFinishSignal();
        }

        //释放空间
        delete dstBuff;
        dstBuff = nullptr;
    }
}
